package com.nyist.server;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import io.micrometer.core.instrument.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 1. 真正实现websocket消息通信的类
 * <p>2. 参考地址: <a href="https://cloud.tencent.com/developer/article/2107965">参考地址</a>
 * <p>3. 在线测试工具地址: <a href="https://websocket.jsonin.com/">工具地址</a>
 * <p>4. ws://127.0.0.1:9008/websocketUrl/789/789
 *
 * @ClassName WebSocketServiceImpl
 * @Description
 * @Author xiaoyang
 * @Date 2023/06/07/20:26
 */
@Slf4j
@Component
@ServerEndpoint(value = "/websocketUrl/{userId}/{uniqueKey}")
public class WebSocketServiceImpl implements WebSocketService {
    /**
     * 静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
     */
    private static int onlineCount = 0;
    /**
     * concurrent包的线程安全，用来存放每个客户端对应的MyWebSocket对象。
     */
    private static final ConcurrentHashMap<String, WebSocketServiceImpl> webSocketConcurrentHashMap = new ConcurrentHashMap<>();
    /** 目标用户离线时发给他的信息存到这里，上线读取
     * key 离线用户id
     * val 整个信息主题
     * */
    // private static final LinkedList<MessageVo> offlineMessage = new LinkedList<>();
    /**
     * 与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    private Session session;
    /**
     * 接收userId
     */
    private String userId = "";
    /**
     * 客户端的随机数, 唯一键
     */
    private String uniqueKey = "";

    /**
     * 连接建立成功调用的方法 , 一定会被调用
     */
    @OnOpen
    @Override
    public void onOpen(Session session
            , @PathParam("userId") String userId
            , @PathParam("uniqueKey") String uniqueKey
    ) {
        this.session = session;
        // this.userId = String.valueOf(session.getUserProperties().get("userId"));
        this.userId = userId;
        this.uniqueKey = uniqueKey;
        // userId 必传, 保证使用此连接的用户都是登录过的
        if (userId == null || "".equals(userId)) {
            log.error("===>>>连接失败,userId为空,session={}", session);
            return;
        }

        // 存在历史信息则更新, 不存在则新增
        if (webSocketConcurrentHashMap.containsKey(userId)) {
            log.info("===>>>存在历史连接,移除老的websocket, userId:{}", userId);
            webSocketConcurrentHashMap.remove(userId);
        } else {
            log.info("===>>>不存在历史连接,需要新建一个连接, userId:{}", userId);
            // 在线数加1
            addOnlineCount();
        }
        webSocketConcurrentHashMap.put(userId, this); // userId 和 当前websocket绑定

        int count = getOnlineCount();
        log.info("===>>>用户连接:userId={},当前在线人数为:count={}", userId, count);
        sendMessage("恭喜你,连接成功!当前在线人数为:" + count);

        // try {
        //     // 查看是否有关于该用户的离线消息
        //     Iterator<MessageVo> iterator = offlineMessage.iterator();
        //     LinkedList<MessageVo> myOfflineMessage = new LinkedList<>();
        //     MessageVo messageVo;
        //     while (iterator.hasNext()){
        //         messageVo = iterator.next();
        //         if (messageVo.getToUserId().equals(userId)){
        //             myOfflineMessage.add(messageVo);
        //             iterator.remove();
        //         }
        //     }
        //     // 如果有关于当前用户的离线消息，就推送
        //     JSONObject jsonObject;
        //     if (myOfflineMessage.size() > 0){
        //         Iterator<MessageVo> iterator2 = myOfflineMessage.iterator();
        //         while (iterator2.hasNext()){
        //             messageVo = iterator2.next();
        //             jsonObject = (JSONObject) JSON.toJSON(messageVo);
        //             sendMessage(jsonObject.toJSONString());
        //             System.out.println("推送离线消息："+jsonObject.toJSONString());
        //         }
        //     }
        // } catch (IOException e) {
        //     log.error("用户:"+userId+",网络异常!!!!!!");
        // }
    }

    /**
     * 连接关闭调用的方法, 正常关闭才会调用, todo: 暂时不知道怎么调用
     */
    @OnClose
    @Override
    public void onClose() {
        // sendMessage("安全退出成功!");
        if (webSocketConcurrentHashMap.containsKey(userId)) {
            webSocketConcurrentHashMap.remove(userId);
            subOnlineCount();
        }
        log.info("用户退出:" + userId + ",当前在线人数为:" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    @Override
    public void onMessage(String message, Session session) {
        log.info("用户:" + this.userId + ",消息原始报文:" + message);
        String returnMsg = "";
        if (StringUtils.isBlank(message)) {
            returnMsg = "消息不能为空!!";
            log.error("===>>>消息为空:userId={}", this.userId);
            sendMessage(returnMsg);
            return;
        }

        if (!message.startsWith("{")) {
            returnMsg = "消息格式错误!!\n必须为JSON格式字符串!!!";
            log.error("===>>>消息格式错误:userId={}", this.userId);
            sendMessage(returnMsg);
            return;
        }

        JSONObject messageJsonObject = null;

        try {
            // 解析发送的报文
            messageJsonObject = JSONUtil.parseObj(message);
        } catch (Exception e) {
            log.error("===>>>解析报文失败:userId={},message={}", this.userId, message);
            log.error("===>>>解析报文失败异常原因为:Exception={}",e.getMessage());
            throw new RuntimeException(e);
        }

        String contentText = messageJsonObject.getStr("contentText");

        if (StringUtils.isBlank(contentText)) {
            returnMsg = "消息不能为空!!\ncontentText不能为空!!";
            log.error("===>>>消息为空:userId={}", userId);
            sendMessage(returnMsg);
            return;
        }

        String toUserId = messageJsonObject.getStr("toUserId");
        // Boolean toAll = messageJsonObject.getBool("toAll");
        if (StringUtils.isBlank(toUserId)) {
            log.info("用户:userId={}, 给全体发送消息,消息内容:msg={}", userId, contentText);
            sendMessageAll(contentText, userId);
            return;
        }

        log.info("用户:fromUserId={}, 给用户:toUserId={},发送消息内容:msg={}", userId, toUserId, contentText);
        if (webSocketConcurrentHashMap.containsKey(toUserId)) {
            // 两种形式都一样, 感觉第一种效率更高
            // webSocketConcurrentHashMap.get(toUserId).sendMessage(contentText);
            // 此种方式需要循环
            sendMessageTo(contentText, toUserId);
        } else {
            returnMsg = "请求的userId:" + toUserId + "不在线!! \n 已记录,当他上线时会收到此条消息: " + contentText;
            log.error(returnMsg);
            sendMessage(returnMsg);
            // 存储到队列
            // offlineMessage.add(messageVo);
        }

        // todo 业务逻辑

        // 消息保存到数据库
        // if (StringUtils.isNotBlank(message)) {
        //     try {
        //         // 解析发送的报文
        //         JSONObject jsonObject = JSON.parseObject(message);
        //         /*封装消息*/
        //         MessageVo messageVo = new MessageVo();
        //         messageVo.setFromUserId(this.userId);
        //         messageVo.setToUserId(jsonObject.getString("toUserId"));
        //         messageVo.setContentText(jsonObject.getString("contentText"));
        //         messageVo.setType("chat");
        //         messageVo.setDate(new Date());
        //
        //         // 传送给对应toUserId用户的websocket
        //         String toUserId = messageVo.getToUserId();
        //         jsonObject = (JSONObject) JSONObject.toJSON(messageVo);
        //
        //         if (StringUtils.isNotBlank(toUserId) && webSocketMap.containsKey(toUserId)) {
        //             System.out.println("转发消息==============" + jsonObject.toJSONString());
        //             webSocketMap.get(toUserId).sendMessage(jsonObject.toJSONString());
        //         } else {
        //             log.error("请求的userId:" + toUserId + "不在线");
        //             // 存储到队列
        //             offlineMessage.add(messageVo);
        //         }
        //         // 将历史消息存储到数据库
        //         messageService.addHistoryChat(messageVo);
        //     } catch (Exception e) {
        //         e.printStackTrace();
        //     }
        // }
    }

    /**
     * @param session
     * @param error
     */
    @OnError
    @Override
    public void onError(Session session, Throwable error) {
        // sendMessageAll("用户异常退出!!!userId="+ this.userId, "admin");
        log.error("用户错误:" + this.userId + ",原因:" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 实现服务器主动推送
     */
    @Override
    public void sendMessage(String message) {
        try {
            log.info("sendMessage: " + message);
            this.session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            log.error("sendMessage error: ", e);
        }
    }

    /**
     * 消息群发
     */
    @Override
    public void sendMessageAll(String message, String fromUserId) {
        try {
            message = "来自 userId=" + fromUserId + " 的全体消息!如下:\n" + message;
            for (WebSocketServiceImpl item : webSocketConcurrentHashMap.values()) {
                item.session.getAsyncRemote().sendText(message);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 点对点
     */
    @Override
    public void sendMessageTo(String message, String toUserId) {
        try {
            for (WebSocketServiceImpl item : webSocketConcurrentHashMap.values()) {
                //    System.out.println("在线人员名单  ："+item.userId.toString());
                if (item.userId.equals(toUserId)) {
                    item.session.getAsyncRemote().sendText(message);
                    break;
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServiceImpl.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServiceImpl.onlineCount--;
    }
}
